#include "Common.h"
#include "AppBase.h"
#include "../Graphics/OpenGL/OpenGLRenderer.h"
#include "../Utils/Depot.h"
#include "../Utils/Logger/Logger.h"
#include "../Utils/Logger/LoggerHtml.h"
#include "../Utils/Logger/LoggerPlainText.h"
#include "../Utils/Timer.h"

using namespace RayFrame;
using namespace glm;

AppBase DLL_DECLARE *RayFrame::g_App = NULL;

AppBase::AppBase():
m_camera(NULL), m_renderer(NULL), m_configure(NULL), m_frame(NULL)
{
  assert(!g_App);
  g_App = this;
}

AppBase::~AppBase()
{
  Depot* d = Depot::Instance();
  SAFE_DELETE( d );

  SAFE_DELETE( m_frame );

  Logger::InstanceRef().WriteBanner("myFramework shutting down");

  Configure::InstanceRef().Destroy(true);
  LOG_CONTENT1("Configuration saved.");

  Logger::InstanceRef().Destroy();
};

bool AppBase::loadRenderer(Renderer::GRAPHIC_TYPE type)
{
  switch(type)
  {
  case Renderer::OPENGL:
    m_renderer = OpenGLRenderer::Instance();
    LOG_CONTENT1("OpenGL Renderer loaded.");
    break;
  case Renderer::DIRECTX9:
  case Renderer::DIRECTX10:
  default:
    ErrorMsg("myFramework support OpenGL only right now.");
    return false;
    break;
  }

  bool result = m_renderer->InitAPI() && m_renderer->Init();

  return result;
}

void AppBase::ResetCamera()
{
  if(!m_camera)
    m_camera = m_renderer->CreateCamera();

  m_camera->Reset();
}

void AppBase::CloseWindow(const bool quit, const bool canUnload)
{
  m_done = quit;

  if(canUnload)
  {
    OnClose();
  }

  m_renderer->ExitAPI();
}

bool AppBase::OnKey(unsigned int key, bool down)
{
  switch(key)
  {
  case KEY_F:
    {
      if(!down)
      {
        static Renderer::RENDER_MODE mode = Renderer::RENDER_POINT;
        g_renderer->SetRenderMode(mode);
        mode = Renderer::RENDER_MODE((mode+1) % Renderer::RENDER_MODE_COUNT);
        return false;
      }
    }
    break;
  }
  return true;
}

bool AppBase::OnMouseMove(int x, int y, int lastX, int lastY)
{
  if( m_camera)
    m_camera->OnMouseMove(x, y);
  return true;
}

void AppBase::OnMouseButton(int x, int y, MouseButton btn, bool down)
{
  m_camera->OnMouseClick(x, y, btn, down);
}

void AppBase::OnMouseWheel(int x, int y, int speed)
{

}

void AppBase::OnResize(int width, int height)
{
  m_renderer->Resize(width, height);
}

void AppBase::BeforeRender()
{
  m_camera->Update();
}

void AppBase::AfterRender()
{
#ifdef _DEBUG
  m_renderer->CheckError();
#endif
}

void RayFrame::AppBase::InitApp()
{
  m_camera = NULL;
  m_done = false;

  Logger::InstanceRef().AddPolicy("myFramework.log", new LoggerPlainText());
  Logger::InstanceRef().AddPolicy("myFramework.html", new LoggerHtml());
  Logger::InstanceRef().WriteBanner("myFramework initializing");

  Configure& cfg = Configure::InstanceRef();
  cfg.Create();

  SetFrame( InitFrame() );

  // TODO: implement different renderers
  loadRenderer(Renderer::OPENGL);

  ResetCamera();
}

void RayFrame::AppBase::SetFrame( RayFrame::FrameBase *frame )
{
  m_frame = frame;
  if(frame)
  {
    assert(frame->IsOK());
    frame->RegisterApp(this);
  }
}

bool RayFrame::AppBase::Tick()
{
  Timer& timer = FrameTimer::InstanceRef();
  timer.Update();
  double timeElapsed = timer.TimeSinceLastUpdate();

  Update(timeElapsed);
  BeforeRender();
  Render(timeElapsed);
  Render2D(timeElapsed);
  m_renderer->SwapBuffer();
  AfterRender();

  return true;
}

void RayFrame::AppBase::Render2D( double timeElapsed )
{
  int showDebug = RayFrame::Configure::InstanceRef().GetValueNum<int>(SHOW_DEBUG_INFO);
  if(showDebug)
    m_renderer->RenderDebugInfo(timeElapsed);
}

FrameBase::FrameBase(vec2 position, vec2 size, STRING caption):
m_pos(position), m_size(size), m_caption(caption), m_app(NULL)
{

}

FrameBase::FrameBase():
m_app(NULL)
{
  m_pos = vec2( GetCfgValueNum(Configure::Cfg_window_x),
    GetCfgValueNum(Configure::Cfg_window_y));
  m_size = vec2( GetCfgValueNum(Configure::Cfg_window_width),
    GetCfgValueNum(Configure::Cfg_window_height));
  m_caption = GetCfgValue(Configure::Cfg_window_caption);
}
